/*-------------------------------------------------------------------------
 *
 * regis.c
 *		快速正则表达式子集
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *	  src/backend/tsearch/regis.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "tsearch/dicts/regis.h"
#include "tsearch/ts_locale.h"

#define RS_IN_ONEOF 1
#define RS_IN_ONEOF_IN	2
#define RS_IN_NONEOF	3
#define RS_IN_WAIT	4


/*
 * 测试正则表达式是否是这里支持的子集。
 * 保持与 RS_compile 同步！
 */
bool RS_isRegis(const char *fc_str)
{
	int			fc_state = RS_IN_WAIT;
	const char *fc_c = fc_str;

	while (*fc_c)
	{
		if (fc_state == RS_IN_WAIT)
		{
			if (t_isalpha(fc_c))
				 /* 可以 */ ;
			else if (t_iseq(fc_c, '['))
				fc_state = RS_IN_ONEOF;
			else
				return false;
		}
		else if (fc_state == RS_IN_ONEOF)
		{
			if (t_iseq(fc_c, '^'))
				fc_state = RS_IN_NONEOF;
			else if (t_isalpha(fc_c))
				fc_state = RS_IN_ONEOF_IN;
			else
				return false;
		}
		else if (fc_state == RS_IN_ONEOF_IN || fc_state == RS_IN_NONEOF)
		{
			if (t_isalpha(fc_c))
				 /* 可以 */ ;
			else if (t_iseq(fc_c, ']'))
				fc_state = RS_IN_WAIT;
			else
				return false;
		}
		else
			elog(ERROR, "internal error in RS_isRegis: state %d", fc_state);
		fc_c += pg_mblen(fc_c);
	}

	return (fc_state == RS_IN_WAIT);
}

static RegisNode *
fc_newRegisNode(RegisNode *fc_prev, int fc_len)
{
	RegisNode  *fc_ptr;

	fc_ptr = (RegisNode *) palloc0(RNHDRSZ + fc_len + 1);
	if (fc_prev)
		fc_prev->next = fc_ptr;
	return fc_ptr;
}

void RS_compile(Regis *fc_r, bool fc_issuffix, const char *fc_str)
{
	int			fc_len = strlen(fc_str);
	int			fc_state = RS_IN_WAIT;
	const char *fc_c = fc_str;
	RegisNode  *fc_ptr = NULL;

	memset(fc_r, 0, sizeof(Regis));
	fc_r->issuffix = (fc_issuffix) ? 1 : 0;

	while (*fc_c)
	{
		if (fc_state == RS_IN_WAIT)
		{
			if (t_isalpha(fc_c))
			{
				if (fc_ptr)
					fc_ptr = fc_newRegisNode(fc_ptr, fc_len);
				else
					fc_ptr = fc_r->node = fc_newRegisNode(NULL, fc_len);
				COPYCHAR(fc_ptr->data, fc_c);
				fc_ptr->type = RSF_ONEOF;
				fc_ptr->len = pg_mblen(fc_c);
			}
			else if (t_iseq(fc_c, '['))
			{
				if (fc_ptr)
					fc_ptr = fc_newRegisNode(fc_ptr, fc_len);
				else
					fc_ptr = fc_r->node = fc_newRegisNode(NULL, fc_len);
				fc_ptr->type = RSF_ONEOF;
				fc_state = RS_IN_ONEOF;
			}
			else				/* 不应该到这里 */
				elog(ERROR, "invalid regis pattern: \"%s\"", fc_str);
		}
		else if (fc_state == RS_IN_ONEOF)
		{
			if (t_iseq(fc_c, '^'))
			{
				fc_ptr->type = RSF_NONEOF;
				fc_state = RS_IN_NONEOF;
			}
			else if (t_isalpha(fc_c))
			{
				COPYCHAR(fc_ptr->data, fc_c);
				fc_ptr->len = pg_mblen(fc_c);
				fc_state = RS_IN_ONEOF_IN;
			}
			else				/* 不应该到这里 */
				elog(ERROR, "invalid regis pattern: \"%s\"", fc_str);
		}
		else if (fc_state == RS_IN_ONEOF_IN || fc_state == RS_IN_NONEOF)
		{
			if (t_isalpha(fc_c))
			{
				COPYCHAR(fc_ptr->data + fc_ptr->len, fc_c);
				fc_ptr->len += pg_mblen(fc_c);
			}
			else if (t_iseq(fc_c, ']'))
				fc_state = RS_IN_WAIT;
			else				/* 不应该到这里 */
				elog(ERROR, "invalid regis pattern: \"%s\"", fc_str);
		}
		else
			elog(ERROR, "internal error in RS_compile: state %d", fc_state);
		fc_c += pg_mblen(fc_c);
	}

	if (fc_state != RS_IN_WAIT)	/* 不应该到这里 */
		elog(ERROR, "invalid regis pattern: \"%s\"", fc_str);

	fc_ptr = fc_r->node;
	while (fc_ptr)
	{
		fc_r->nchar++;
		fc_ptr = fc_ptr->next;
	}
}

void RS_free(Regis *fc_r)
{
	RegisNode  *fc_ptr = fc_r->node,
			   *fc_tmp;

	while (fc_ptr)
	{
		fc_tmp = fc_ptr->next;
		pfree(fc_ptr);
		fc_ptr = fc_tmp;
	}

	fc_r->node = NULL;
}

static bool fc_mb_strchr(char *fc_str, char *fc_c)
{
	int			fc_clen,
				fc_plen,
				fc_i;
	char	   *fc_ptr = fc_str;
	bool		fc_res = false;

	fc_clen = pg_mblen(fc_c);
	while (*fc_ptr && !fc_res)
	{
		fc_plen = pg_mblen(fc_ptr);
		if (fc_plen == fc_clen)
		{
			fc_i = fc_plen;
			fc_res = true;
			while (fc_i--)
				if (*(fc_ptr + fc_i) != *(fc_c + fc_i))
				{
					fc_res = false;
					break;
				}
		}

		fc_ptr += fc_plen;
	}

	return fc_res;
}

bool RS_execute(Regis *fc_r, char *fc_str)
{
	RegisNode  *fc_ptr = fc_r->node;
	char	   *fc_c = fc_str;
	int			fc_len = 0;

	while (*fc_c)
	{
		fc_len++;
		fc_c += pg_mblen(fc_c);
	}

	if (fc_len < fc_r->nchar)
		return 0;

	fc_c = fc_str;
	if (fc_r->issuffix)
	{
		fc_len -= fc_r->nchar;
		while (fc_len-- > 0)
			fc_c += pg_mblen(fc_c);
	}


	while (fc_ptr)
	{
		switch (fc_ptr->type)
		{
			case RSF_ONEOF:
				if (!fc_mb_strchr((char *) fc_ptr->data, fc_c))
					return false;
				break;
			case RSF_NONEOF:
				if (fc_mb_strchr((char *) fc_ptr->data, fc_c))
					return false;
				break;
			default:
				elog(ERROR, "unrecognized regis node type: %d", fc_ptr->type);
		}
		fc_ptr = fc_ptr->next;
		fc_c += pg_mblen(fc_c);
	}

	return true;
}
